Introduction to R Shiny Package

Hakan Turgay & Bertan Taylan

Chapter 1

Motivation, Basic R-Shiny Package and Example

Motivation

  • Scientists mostly use R to process their analyzes

  • Presenting/Sharing their findings are usually done in static format

  • Problem : They cannot present additional questions directly

  • Simple idea: Immigrating ratios from a specific region of Turkey due to years and also forecasts about these ratios (e.g. Journalist)

What is Shiny Package?

  • Shiny is an R package that makes it easy to build interactive web applications (apps) straight from R. This lesson will get you started building Shiny apps right away.
  • Build useful web applications with only a few lines of code—no JavaScript required.
  • Shiny applications are automatically “live” in the same way that spreadsheets are live. Outputs change instantly as users modify inputs, without requiring a reload of the browser.
  • Shiny user interfaces can be built entirely using R, or can be written directly in HTML, CSS, and JavaScript for more flexibility.
  • Pre-built output widgets for displaying plots, tables, and printed output of R objects.

Basically


R Shiny = R + interactivity + web made easy

kmeans

Install Shiny Package

If you still haven’t installed the Shiny package, open an R session, connect to the internet, and run

install.packages("shiny")

First App in Shiny

library(shiny)
runExample("01_hello")

Code Output

firstapp

More Example

runExample("01_hello")      # a histogram
runExample("02_text")       # tables and data frames
runExample("03_reactivity") # a reactive expression
runExample("04_mpg")        # global variables
runExample("05_sliders")    # slider bars
runExample("06_tabsets")    # tabbed panels
runExample("07_widgets")    # help text and submit buttons
runExample("08_html")       # Shiny app built from HTML
runExample("09_upload")     # file upload wizard
runExample("10_download")   # file download wizard
runExample("11_timer")      # an automated timer

Chapter 2

Structure of a Shiny App

Main Structure

  • ui: Nested R functions that assemble an HTML user interface for the app

  • server : A function with instructions on how to build and rebuild the R objects displayed in the UI

  • shinyApp : Combines ui and server into a functioning app

  • Save the template as app.R

  • a call to the shinyApp function

    library(shiny)
    
    ui <- fluidPage(
     
    )
    
    server <- function(input, output, session) {
      
    }
    
    shinyApp(ui, server)

Alternative Approach

kmeans

Say Hello with Shiny

library(shiny)
ui <- fluidPage(
    "Hello Shiny Package"
    )

server <- function(input, output) {
    
}

shinyApp(ui = ui, server = server)

Code Output

helloshiny

Chapter 3

Inputs and Outputs

Input and Project Template

  • Basically, inputs are values which are provided by users. We will try to get this value from UI with components of R-Shiny and with these information, we are going to create an “output” from server side. After that we will try to trigger something on UI with this output.
  • First, we will start with one of the R Shiny’s templates.
library(shiny)

ui <-  fluidPage()

server <- function(input,output){
  
}

shinyApp(ui = ui, server = server)

Define Input Component

  • Let’s form an input component. We chose radioButtons component for this explanation.

library(shiny)

ui <-  fluidPage(
  radioButtons(inputId = "university", label = "Choose an university",
                choices =  c("ITU" = "Istabul Teknik",
                                    "BOUN" = "Bogazici",
                                    "ODTU" = "Ortadogu Teknik")),
)

server <- function(input,output){
  
}

shinyApp(ui = ui, server = server)

and More Input Component

Input Types

Input Syntax

Input syntax

Define Output

  • Our aim on this example is printing university logo on specific location on UI according to information was retrieved with input.
library(shiny)

ui <-  fluidPage(
  radioButtons(inputId = "university", label = "Choose an university",
                choices =  c("ITU" = "Istabul Teknik",
                              "BOUN" = "Bogazici",
                              "ODTU" = "Ortadogu Teknik")),
  imageOutput("universityLogo",height = 300,width = 300),
  
  
)
server <- function(input,output){

}
shinyApp(ui = ui, server = server)

and More Output Options

  • Different output options can be seen from this table:

Output types

Define Render on Server Side

  • Now we will read the input value and update output area with render function on server side.
 library(shiny)

ui <-  fluidPage(
  radioButtons(inputId = "university", label = "Choose an university",
                choices =  c("Istabul Technical University" = "ITU",
                             "Bogazici University" = "BOUN",
                             "Middle East Technical University" = "ODTU" )),
  imageOutput("universityLogo",height = 600,width = 600),
  
  
)

server <- function(input,output){
  output$universityLogo <- renderImage({
    if (is.null(input$university))
      return(NULL)
    
    if (input$university == "ITU") {
      return(list(
        src = "./images/university/itu.png",
        contentType = "image/png",
        alt = "ITU"
      ))
    } else if (input$university == "BOUN") {
      return(list(
        src = "./images/university/boun.png",
        filetype = "image/png",
        alt = "Bogazici"
      ))
    } else if (input$university == "ODTU") {
      return(list(
        src = "./images/university/odtu.png",
        filetype = "image/pnd",
        alt = "Ortadogu Teknik"
      ))
    }
    
    
  }, deleteFile = FALSE)
  
}

shinyApp(ui = ui, server = server)

and More Render Method Examples

render

Overview UI

res

Overview Server

res

Code Output

  • As result, we retrieved a selection of a university from UI and we passed this information to imageRender function to print the university’s logo again on UI.
res

Reviewing an Example

library(shiny)
runExample("02_text")
kmeans

Starting to Code Analysis

kmeans

Change code

library(shiny)

ui <- fluidPage(
  titlePanel("Shiny Text"),
  sidebarLayout(
    
    sidebarPanel(
      
      checkboxGroupInput("dataset", "Choose a dataset:",
                         c("rock", "pressure", "cars"),
                         selected = "rock"),
                         
      numericInput(inputId = "obs",
                   label = "Number of observations to view:",
                   value = 10)
    ),
    
    mainPanel(
      verbatimTextOutput("summary"),
      tableOutput("view")

Result

check

Chapter 4

Improving the Design

Design Part

  • Assemble UI with HTML/CSS/…

  • Adjustment of the layout scheme

Grid Layout

  • This feature is a component of content part.
  • Rows are created by the fluidRow() function and columns defined by the column() function.
  • First parameter of column function is its width (which can take values between 1 and 12).
  • grid

Tabsets

  • Tabsets are good alternatives in need of subdividing the user interface into discrete sections and are components of content part.
  • tabsetPanel function can be used for creating Tabsets.
  • tabset

Chapter 5

Implementations in Shiny: JavaScript, HTML and CSS

Using HTML in R Shiny


In R, HTML elements can be defined by tags keyword.
ui <fluidPage (
tags$h1(”R Shiny Introduction”) ,
tags$hr () ,
tags$br () ,
tags$p(strong(”Istanbul Technical University”)),
tags$p(em(”Mathematical Engineering”)),
tags$a(href=”https://www.itu.edu.tr”,”Website”))
server <function(input , output){} > 
shinyApp(ui = ui , server = server)

tags

…and more Html Tag

html tag

External file import (html, css , js ext)

Method of importing depends on type of the file;

To include a CSS file


use includeCSS() or
1. Place the file in the www subdirectory
2. Link to it with:

tags$head(tags$link(rel = "stylesheet",
type = "text/css", href = "<file name>"))

To include JavaScript


use includeScript() or
1. Place the file in the www subdirectory
2. Link to it with:

tags$head(tags$script(src = "<file name>"))

To include HTML file

includeHTML("include.html")

Example of Including Javascript

//myscripts.js file
document.body.style.backgroundColor = "skyblue";

javascript

Result

kmeans

Chapter 6

Introduction to HTMLWidgets Package

Overview

  • This package builds a framework for creating R bindings to JavaScript libraries. HTMLWidgets can be
    • Used at the R console for data analysis
    • Embedded within R Markdown documents
    • Incorporated into Shiny web applications

Creating a Widget

  • Three elements forms the widget:

    • Dependencies : JavaScript and CSS assets will be used by the widget
    • R binding : This is the function where R stuff happens
    • JavaScript binding : JavaScript code that connects everything and passes the data and the choices collected from the R binding to the JavaScript library which is (are) using in widget

Some most-used HTMLWidget Examples

kmeans


leaflet

kmeans


dygraph

Small Case Study: Predicted Deaths From Lung Disease By Using Dygraph Package

UI part of the code:

ui <- fluidPage(
  
  titlePanel("Predicted Deaths from Lung Disease (UK)"),
  
  sidebarLayout(
    sidebarPanel(
      numericInput("months", label = "Months to Predict", 
                   value = 72, min = 12, max = 144, step = 12),
      selectInput("interval", label = "Prediction Interval",
                  choices = c("0.80", "0.90", "0.95", "0.99"),
                  selected = "0.95"),
      checkboxInput("showgrid", label = "Show Grid", value = TRUE),
      hr(),
      div(strong("From: "), textOutput("from", inline = TRUE)),
      div(strong("To: "), textOutput("to", inline = TRUE)),
      div(strong("Date clicked: "), textOutput("clicked", inline = TRUE)),
      div(strong("Nearest point clicked: "), textOutput("point", inline = TRUE)),
      br(),
      helpText("Click and drag to zoom in (double click to zoom back out).")
    ),
    mainPanel(
      dygraphOutput("dygraph")
    )

Server part of the code

server <- function(input, output) {
  
  predicted <- reactive({
    hw <- HoltWinters(ldeaths)
    predict(hw, n.ahead = input$months, 
            prediction.interval = TRUE,
            level = as.numeric(input$interval))
  })
  output$dygraph <- renderDygraph({
    dygraph(predicted(), main = "Predicted Deaths/Month") %>%
      dySeries(c("lwr", "fit", "upr"), label = "Deaths") %>%
      dyOptions(drawGrid = input$showgrid)
  })
  output$from <- renderText({
    strftime(req(input$dygraph_date_window[[1]]), "%d %b %Y")      
  })
  output$to <- renderText({
    strftime(req(input$dygraph_date_window[[2]]), "%d %b %Y")
  })
  output$clicked <- renderText({
    strftime(req(input$dygraph_click$x), "%d %b %Y")
  })
  output$point <- renderText({
    paste0('X = ', strftime(req(input$dygraph_click$x_closest_point), "%d %b %Y"), 
           '; Y = ', req(input$dygraph_click$y_closest_point))
  })

Result

check

Creating a widget step-by-step

Requirements

  • In order to create a new widget, first we have to install htmlwidgets package. To install it from R console;
install.packages("htmlwidgets")

Scaffolding

To create a new widget you can call the scaffoldWidget function to generate the basic structure for your widget. This function will:

  • Create the .R, .js, and .yaml files required for your widget
  • Tip: If provided, take a Bower package (which is a package manager for web) name and automatically download the JavaScript library (and its dependencies) and add the required entries to the .yaml file. This method is highly preferrable because it guarantees that you get started with the right file structure.

MyWidget

We want to create a widget named ‘mywidget’ in a new package of the same name:

devtools::create("mywidget")               # create package using devtools
setwd("mywidget")                          # navigate to package dir
htmlwidgets::scaffoldWidget("mywidget")    # create widget scaffolding
devtools::install()                        # install the package so we can try it
  • This creates a simple widget that takes a single text argument and displays that text within the widgets HTML element. You can try it like this:
library(mywidget)
mywidget("hello, world")
  • This is the most minimal widget possible and doesn’t yet include a JavaScript library.

Chapter 7

Publishing Project

Keeping Project in Github

  • Because of their dynamic structure, projects created with R Shiny need platforms which can run R codes. This means, by its nature, extra costs.

  • If we choose to keep our project on a specific location and provide the ability of running the project on users’ locals, first we have to put our project to Github (it must definitely have app.r or server.R files) and guide the users to run these codes on their R console:

library(shiny)
runGithub("<repo name of project>","<github username of project owner>")

Publishing as a Web Site

  • We need a server and a domain to publish our project as a website. There are many paid and considerably less unpaid services to make it happen. Also, R Studio provides following options to its developers:
    • shinyapps.io
    • Shiny Server
    • R Studio Connect

Using shinyapps.io

  • This platform is a paid service, but we have the chance to host one application for free.

  • It is easy to use and works pretty fast.

  • To use this platform, first we have to create user account from this link: https://www.shinyapps.io

  • After logging in, we should get token information from token tab located on up-right corner of the page.

  • Then, we should write token info and following code on our R Shiny projects console panel, respectively:

library(rsconnect)
rsconnect::setAccountInfo(name="<ACCOUNT>",token="<TOKEN>",secret="<SECRET>")
  • And to deploy:
deployApp()

shinyapp.io From R Studio

  • If we give token properly, now we can share our work with just one click.
check

Using Shiny Server ve R Studio Connect

  • Shiny Server : It is a free project being developed open source. Let’s assume we rented a physical machine (e.g. computers) and we want to run R codes which feed our project. In this case we have to send the Shiny Server’s (R) virtual image to our rented machine.

  • Back-end part of the project can be done by this method, but to create front-end, we will need to purchase a domain (website name).

  • R Studio Connect : is a paid service also developed by R Studio. If we plan to make profits from our applications, we can access some ready to use services by this platform.

Chapter 8

Case Studies

Case Study: Childlessness and Gender Gap By Using Ggraph Package

  • In this section, we will build interactive world maps and show these in the form of an Shiny app.
  • At first, we’re going to start with importing, exploring and cleaning the data that datasets we’re going to use. Since the data sets are dirty, we have to get them in a better shape to make more useful for us.
  • Final forms of our datasets will be like this:

    example

Datasets

example

Creating a function to build map

  • Next, it’s time to define the function that we’ll use for building our world maps. The inputs to this function are the merged data frame, the world data containing geographical coordinates, and the data type, period and indicator the user will select in the R Shiny app.
worldMaps <- function(df, world_data, data_type, period, indicator){
 
 # Function for setting the aesthetics of the plot
 my_theme <- function () { 
   theme_bw() + theme(axis.text = element_text(size = 14),
                      axis.title = element_text(size = 14),
                      strip.text = element_text(size = 14),
                      panel.grid.major = element_blank(), 
                      panel.grid.minor = element_blank(),
                      panel.background = element_blank(), 
                      legend.position = "bottom",
                      panel.border = element_blank(), 
                      strip.background = element_rect(fill = 'white', colour = 'white'))
 }

Function Continues:

  # Select only the data that the user has selected to view
  plotdf <- df[df$Indicator == indicator & df$DataType == data_type & df$Period == period,]
  plotdf <- plotdf[!is.na(plotdf$ISO3), ]
  # Add the data the user wants to see to the geographical world data
  world_data['DataType'] <- rep(data_type, nrow(world_data))
  world_data['Period'] <- rep(period, nrow(world_data))
  world_data['Indicator'] <- rep(indicator, nrow(world_data))
  world_data['Value'] <- plotdf$Value[match(world_data$ISO3, plotdf$ISO3)]
  
  # Create caption with the data source to show underneath the map
  capt <- paste0("Source: ", ifelse(data_type == "Childlessness", "United Nations" , "World Bank"))
 # Specify the plot for the world map
  library(RColorBrewer)
  library(ggiraph)
  g <- ggplot() + 
    geom_polygon_interactive(data = world_data, color = 'gray70', size = 0.1,
                                    aes(x = long, y = lat, fill = Value, group = group, 
                                        tooltip = sprintf("%s<br/>%s", ISO3, Value))) + 
    scale_fill_gradientn(colours = brewer.pal(5, "RdBu"), na.value = 'white') + 
    labs(fill = data_type, color = data_type, title = NULL, x = NULL, y = NULL, caption = capt) + 
    my_theme()
  return(g)
}

Last Step: Building the Shiny App

  • Now we put our data in a good shape and world mapping function ready and specified.
  • Let’s start with UI.
# Define the UI
ui = fluidPage(
  
  # App title
  titlePanel("Childlessness and Gender Gap Index Data"),
  
  # Sidebar layout with input and output definitions
  sidebarLayout(
    
    # Sidebar panel for inputs 
    sidebarPanel(
      
      # First input: Type of data
      selectInput(inputId = "data_type",
                  label = "Choose the type of data you want to see:",
                  choices = list("Childlessness" = "Childlessness", "Gender Gap Index" = "Gender Gap Index")),
      
      # Second input (choices depend on the choice for the first input)
      uiOutput("secondSelection"),
      
      # Third input (choices depend on the choice for the first and second input)
      uiOutput("thirdSelection")
    ),

UI Continues with Main Panel:

 # Main panel for displaying outputs
    mainPanel(
      
      # Hide errors
      tags$style(type = "text/css",
                 ".shiny-output-error { visibility: hidden; }",
                 ".shiny-output-error:before { visibility: hidden; }"),
      
      # Output: interactive world map
      girafeOutput("distPlot")
      
    )
  )
)

Server side of the app

# Define the server
server = function(input, output) {
  
  # Create the interactive world map
  output$distPlot <- renderGirafe({
    ggiraph(code = print(worldMaps(df, world_data, input$data_type, input$period, input$indicator)))
  })
  
  # Change the choices for the second selection on the basis of the input to the first selection
  output$secondSelection <- renderUI({
    choice_second <- as.list(unique(df$Period[which(df$DataType == input$data_type)]))
    selectInput(inputId = "period", choices = choice_second,
                label = "Choose the period for which you want to see the data:")
  })
  
  # Change the choices for the third selection on the basis of the input to the first and second selections
  output$thirdSelection <- renderUI({
    lab <- ifelse(input$data_type == "Childlessness", "age group", "indicator")
    choice_third <- as.list(unique(df$Indicator[df$DataType == input$data_type & df$Period == input$period]))
    selectInput(inputId = "indicator", choices = choice_third,
                label = paste0("Choose the type of ", lab, " you want to explore:"))
  })
}

Result

example

Case Study: Earthquake Map By Using Leaflet Package

  • screenshot

Distinguishing Depths

library(shiny)
library(leaflet)
library(dplyr)
library(leaflet.extras)

data <- read.csv("./data/data.csv")
country <- data["place"]

data$depth_type <-      ## 
    ifelse(
        data$depth <= 70,
        "shallow",
        ifelse(
            data$depth <= 300 | data$depth > 70,
            "intermediate",
            ifelse(data$depth > 300, "deep", "other")

Forming the UI

ui <- fluidPage(mainPanel(
    #this will create a space for us to display our map
    leafletOutput(outputId = "mymap"),
    #this allows us to put the checkmarks on top of the map to
    #allow people to view earthquake depth or overlay a heatmap.
    absolutePanel(
        top = 250,
        left = 30,
        checkboxInput("markers", "Depth", FALSE),
        checkboxInput("heat", "Heatmap", FALSE)
    )
))

Server part

server <- function(input, output, session) {
    #define the color palette for the magnitude of the earthquake
    pal <- colorNumeric(
        palette = c(
            'gold',
            'orange',
            'dark orange',
            'orange red',
            'red',
            'dark red'
        ),
        domain = data$mag
    )
    #define the color of for the depth of the earthquakes
    pal2 <- colorFactor(palette = c('blue', 'yellow', 'red'),
                        domain = data$depth_type)

Creating the Map

output$mymap <- renderLeaflet({
leaflet(data) %>%
  addTiles() %>%
  addFullscreenControl(position = "topleft", pseudoFullscreen = TRUE) %>%
    setView(lng = 35, lat = 42, zoom = 5)  %>% #setting the view over
      addTiles() %>%
      addCircles(
          data = data,
          lat = ~ latitude,
          lng = ~ longitude,
          weight = 0.5,
          radius = ~ sqrt(mag) * 15000,
          popup = ~ as.character(mag),
          label = ~ as.character(paste0("Magnitude: ", sep = " ", mag)),
          color = ~ pal(mag),
          fillOpacity = 0.5

Making Checkboxes Dynamic With observe Function

observe({
        proxy <- leafletProxy("mymap", data = data)
        proxy %>% clearMarkers()
        if (input$markers) {
            proxy %>% addCircleMarkers(
                stroke = FALSE,
                color = ~ pal2(depth_type),
                fillOpacity = 0.2,
                label = ~ as.character(paste0("Magnitude: ", sep = " ", mag))
            ) %>%
                addLegend(
                    "bottomright",
                    pal = pal2,
                    values = data$depth_type,
                    title = "Depth Type",
                    opacity = 1
                )
        }
        else {
            proxy %>% clearMarkers() %>% clearControls()
        }

Map Adjustments with Using leafletproxy Function

observe({
        proxy <- leafletProxy("mymap", data = data)
        proxy %>% clearMarkers()
        if (input$heat) {
            proxy %>%  addHeatmap(
                lng =  ~ longitude,
                lat =  ~ latitude,
                intensity = ~ mag,
                blur =  10,
                max = 0.05,
                radius = 15
            )
        }
        else{
            proxy %>% clearHeatmap()
        }

Result

screenshot

Case Study: Migration By Using Leaflet Package

Using Leaflet

  • For creating a map we used Leaflet library.
  • Leaflet is a open source JavaScript library for creating interactive maps.
  • It can be implemented and used in Shiny easily.
  • Leaflet gives the opportunity of adding informational popups to particular part of maps.
  • leaflet

Result

result